
import sys
sys.stdout.reconfigure(encoding='utf-8')
sys.stderr.reconfigure(encoding='utf-8')

import os
import time
import subprocess
import sys
import logging
import threading
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from datetime import datetime  # 修改导入方式

# 导入托盘图标相关库
import pystray
from PIL import Image

#导入更改进程名相关库
# import os
# from setproctitle import setproctitle

# 全局变量用于存储日志文件路径
info_log_path = None

def setup_logging():
    """配置INFO日志"""
    base_log_dir = "AutoPlayBellLog"
    os.makedirs(base_log_dir, exist_ok=True)

    now = datetime.now()
    info_date_dir = os.path.join(base_log_dir, "INFO", now.strftime("%Y"), now.strftime("%m"), now.strftime("%d"))
    os.makedirs(info_date_dir, exist_ok=True)

    timestamp = now.strftime("%Y%m%d_%H-%M-%S_%f")
    info_log = os.path.join(info_date_dir, f"{timestamp}.log")

    logger = logging.getLogger()
    logger.setLevel(logging.INFO)

    # 清除已有的处理器
    if logger.handlers:
        logger.handlers.clear()

    # INFO处理器 - 使用自定义格式，不包含时间戳和级别
    info_handler = logging.FileHandler(info_log, encoding='utf-8')
    info_formatter = logging.Formatter('%(message)s')
    info_handler.setFormatter(info_formatter)
    logger.addHandler(info_handler)

    # 控制台处理器
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    console_formatter = logging.Formatter('%(message)s')
    console_handler.setFormatter(console_formatter)
    logger.addHandler(console_handler)

    # 更新全局日志路径
    global info_log_path
    info_log_path = info_log

    return logger, info_log

def reset_logging():
    """重新配置日志，创建新一天的日志文件"""
    logger = logging.getLogger()
    
    # 清除所有处理器
    if logger.handlers:
        for handler in logger.handlers[:]:
            handler.close()
            logger.removeHandler(handler)
    
    # 重新设置日志
    return setup_logging()

def create_level_log(logger, level, message, trace=None):
    """创建WARNING或ERROR日志"""
    base_log_dir = "AutoPlayBellLog"
    dir_name = "WARNING" if level == logging.WARNING else "ERROR"
    dir_path = os.path.join(base_log_dir, dir_name)
    os.makedirs(dir_path, exist_ok=True)

    timestamp = datetime.now().strftime("%Y%m%d_%H-%M-%S_%f")
    log_file = os.path.join(dir_path, f"{timestamp}.log")

    handler = logging.FileHandler(log_file, encoding='utf-8')
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
    handler.setFormatter(formatter)
    logger.addHandler(handler)

    if level == logging.WARNING:
        logger.warning(message)
    else:
        logger.error(message)
        if trace:
            logger.error(trace)

    logger.removeHandler(handler)

class ConfigWatcher(FileSystemEventHandler):
    def __init__(self, callback, logger):
        self.callback = callback
        self.logger = logger

    def on_modified(self, event):
        if event.src_path.endswith('.ini'):
            self.callback()

class BellScheduler:
    def __init__(self, logger):
        # 更改配置目录为daily_configs，以匹配实际配置文件位置
        self.config_dir = 'daily_configs'
        # 使用固定英文星期缩写列表，不受系统区域设置影响
        weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
        self.current_day = weekdays[datetime.now().weekday()]
        self.schedules = self.load_schedules(logger)
        self.running = True
        self.use_python = self.check_python_available(logger)
        self.logger = logger
        # 初始化Python路径
        self.python_path = os.path.abspath("./runtime/python.exe")

    def check_python_available(self, logger):
        try:
            subprocess.run([self.python_path, '--version'], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            return True
        except:
            return False

    def load_schedules(self, logger):
        schedules = {}
        # 直接从daily_configs目录读取当前星期的ini文件，而非子目录
        config_file = os.path.join(self.config_dir, f"{self.current_day}.ini")

        logger.info(f"加载配置文件: {config_file}")

        if not os.path.exists(config_file):
            logger.warning(f"未找到 {config_file} 配置文件")
            return schedules

        try:
            with open(config_file, 'r', encoding='utf-8') as f:
                content = f.read().strip()
                logger.info(f"文件内容: {content}")

            # 解析INI格式，提取[TIME]节下的时间
            in_time_section = False
            for line in content.split('\n'):
                line = line.strip()
                if line.startswith('[TIME]'):
                    in_time_section = True
                    continue
                if in_time_section and line:
                    # 解析时间=铃声路径格式
                    if '=' in line:
                        time_part, mp3_file = line.split('=', 1)
                        time_part = time_part.strip()
                        mp3_file = mp3_file.strip()
                        schedules[time_part] = mp3_file
                        logger.info(f"  时间 {time_part} 映射到铃声: {mp3_file}")

            logger.info(f"  已添加 {len(schedules)} 个时间点")

        except Exception as e:
            logger.error(f"加载 {config_file} 时出错: {e}")

        return schedules

    def reload_schedules(self):
        # 重新加载时更新当前日期，以处理跨天情况
        weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
        self.current_day = weekdays[datetime.now().weekday()]
        self.schedules = self.load_schedules(self.logger)
        self.logger.info("配置已重新加载")
        
    def reset_daily_log(self):
        """创建新的日志文件"""
        try:
            old_log_path = info_log_path
            new_logger, new_log_path = reset_logging()
            self.logger = new_logger
            self.logger.info("=" * 50)
            self.logger.info(f"创建新日志文件: {new_log_path}")
            if old_log_path:
                self.logger.info(f"旧日志文件: {old_log_path}")
            self.logger.info("=" * 50)
        except Exception as e:
            # 如果重配置失败，使用原logger记录错误
            self.logger.error(f"重新配置日志失败: {e}")

    def play_bell(self, mp3_file):
        # 检查音频文件是否存在
        if not os.path.exists(mp3_file):
            self.logger.error(f"音频文件不存在: {mp3_file}")
            return
            
        # 使用runtime目录下的python.exe运行BellPlayer.py
        bell_player_path = os.path.abspath("BellPlayer.py")
        cmd = [self.python_path, bell_player_path, '-f', mp3_file]

        # 记录BellPlayer调用信息
        self.logger.info("\n==============BellPlayerLog=====================")
        self.logger.info(f"函数被调用: 文件=./{os.path.basename(mp3_file)}, 音量=1.0")
        self.logger.info(f"当前工作目录: {os.getcwd()}")
        self.logger.info(f"Python可执行文件路径: {self.python_path}")
        self.logger.info("==================================================")

        try:
            # 捕获标准输出和错误
            result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            
            # 尝试解码输出
            try:
                stdout = result.stdout.decode('utf-8')
            except UnicodeDecodeError:
                stdout = result.stdout.decode('gbk', errors='replace')
            
            # 过滤掉ANSI转义序列
            import re
            ansi_escape = re.compile(r'\x1B(?:[@-Z\-_]|\[[0-?]*[ -/]*[@-~])')
            cleaned_output = ansi_escape.sub('', stdout)
            
            # 只保留BellPlayer的日志输出，去掉空行
            bell_player_logs = []
            capture = False
            for line in cleaned_output.split('\n'):
                line = line.strip()
                if not line:  # 跳过空行
                    continue
                if '程序启动:' in line:
                    capture = True
                if capture:
                    bell_player_logs.append(line)
                    if '程序结束:' in line:
                        break
            
            # 记录BellPlayer的日志
            for log in bell_player_logs:
                self.logger.info(log)
            
            self.logger.info("\n\n")
        except FileNotFoundError:
            self.logger.error(f"未找到Python解释器: {self.python_path} 或 BellPlayer.py")

    def check_time(self):
        now = datetime.now()
        current_time = now.strftime('%H:%M:%S')

        if current_time in self.schedules:
            mp3_file = self.schedules[current_time]
            self.play_bell(mp3_file)
            # 避免同一秒内重复触发
            time.sleep(1)

    def start_watcher(self):
        event_handler = ConfigWatcher(self.reload_schedules, self.logger)
        observer = Observer()
        # 监控daily_configs目录下的配置文件变化
        observer.schedule(event_handler, path=self.config_dir, recursive=False)
        observer.start()
        return observer

    def run(self):
        # 移除启动信息，避免干扰BellPlayer日志格式
        pass

        # 将观察者保存为类属性，以便在exit_app中访问
        self.observer = self.start_watcher()

        # 启动托盘图标
        create_tray_icon(self.logger)

        try:
            # 初始化上次检查的日期
            weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
            self.last_day = weekdays[datetime.now().weekday()]
            
            # 检查当前时间是否为0点，如果是则立即创建新日志
            current_time = datetime.now().strftime('%H:%M:%S')
            if current_time == '00:00:00':
                self.reset_daily_log()
            
            while self.running:
                # 检查是否到新的一天（0点触发）
                current_day = weekdays[datetime.now().weekday()]
                if current_day != self.last_day:
                    self.logger.info("=" * 50)
                    self.logger.info(f"每日重置开始: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
                    self.reset_daily_log()  # 先创建新的日志文件
                    self.reload_schedules()  # 再重载配置文件
                    self.last_day = current_day
                    self.logger.info("每日重置完成")
                    self.logger.info("=" * 50)
                else:
                    # 同一天内，只在0:00:00时刻检查是否需要额外重置
                    current_time = datetime.now().strftime('%H:%M:%S')
                    if current_time == '00:00:00':
                        self.logger.info("=" * 50)
                        self.logger.info(f"0点重置开始: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
                        self.reset_daily_log()  # 先创建新的日志文件
                        self.reload_schedules()  # 再重载配置文件
                        self.logger.info("0点重置完成")
                        self.logger.info("=" * 50)
                        time.sleep(1)  # 避免重复触发
                
                self.check_time()
                time.sleep(0.1)  # 检查频率提高到每100毫秒
            self.logger.info("主循环已退出，running标志为False")
        finally:
            # 确保观察者线程被停止
            self.observer.stop()
            self.observer.join()
            self.logger.info("观察者线程已停止")
            self.logger.info("程序结束: " + datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
            self.logger.info("=" * 50)

# 托盘图标相关函数

def open_log(icon=None, item=None):
    """打开日志文件"""
    global info_log_path
    if info_log_path and os.path.exists(info_log_path):
        try:
            # 在Windows上使用默认程序打开日志文件
            os.startfile(info_log_path)
            logging.info(f"已打开日志文件: {info_log_path}")
        except Exception as e:
            logging.error(f"无法打开日志文件: {e}")
    else:
        logging.warning("日志文件不存在或路径未设置")


def exit_app(icon, item):
    """退出应用程序"""
    logging.info("正在退出自动播放系统...")
    icon.stop()
    # 设置全局标志以停止主循环
    if hasattr(sys, 'bell_scheduler'):
        scheduler = sys.bell_scheduler
        scheduler.running = False
        logging.info("已设置running标志为False")
    else:
        logging.warning("未找到bell_scheduler实例")
    # 等待一小段时间确保清理完成
    time.sleep(0.5)
    # 不使用sys.exit，让程序自然退出
    logging.info("自动播放系统已退出")


def create_tray_icon(logger):
    """创建托盘图标"""
    try:
        # 加载图标
        icon_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'icon', 'autobell.ico')
        if os.path.exists(icon_path):
            image = Image.open(icon_path)
            logger.info(f"已加载托盘图标: {icon_path}")
        else:
            logger.warning(f"未找到图标文件: {icon_path}，使用默认图标")
            # 创建一个简单的红色图标作为备用
            image = Image.new('RGB', (64, 64), color='red')

        # 创建菜单
        menu = (
            pystray.MenuItem('打开日志', open_log),
            pystray.Menu.SEPARATOR,
            pystray.MenuItem('退出自动播放', exit_app)
        )

        # 创建图标对象
        icon = pystray.Icon("AutoPlayBell", image, "自动铃声播放", menu)

        # 设置左键点击事件
        icon.left_click = open_log

        # 在单独的线程中运行托盘图标
        def run_icon():
            icon.run()

        icon_thread = threading.Thread(target=run_icon, daemon=True)
        icon_thread.start()
        logger.info("托盘图标已启动")

    except Exception as e:
        logger.error(f"创建托盘图标时出错: {e}")
        self.running = False
        observer.stop()
        observer.join()

if __name__ == "__main__":
    # 设置进程名称
    #setproctitle('gbz_autoplaybell')
    try:
        logger, info_log = setup_logging()
        logger.info("=" * 50)
        logger.info(f"程序启动: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        logger.info(f"INFO日志文件: {info_log}")

        scheduler = BellScheduler(logger)
        # 将scheduler实例存储在sys模块中，以便在exit_app中访问
        sys.bell_scheduler = scheduler
        scheduler.run()

        logger.info(f"托盘图标已退出: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        logger.info("=" * 50)

        for handler in logging.getLogger().handlers[:]:
            logging.getLogger().removeHandler(handler)
        sys.stdout.flush()
    except Exception as e:
        import traceback
        logger, _ = setup_logging()
        create_level_log(logger, logging.ERROR, f"系统错误: {e}", traceback.format_exc())
        for handler in logging.getLogger().handlers[:]:
            logging.getLogger().removeHandler(handler)
        sys.exit(1)